
/* GCSx
** COLOR.H
**
** Color conversion and selection widget
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_COLOR_H_
#define __GCSx_COLOR_H_

// Color structure

class color {
private:
    float H; // 0.0 to 1.0
    float S; // 0.0 to 1.0
    float L; // 0.0 to 1.0
    
public:
    int max;
    int maxAlpha;

    int R; // 0 to max
    int G; // 0 to max
    int B; // 0 to max

    int Hi; // 0 to 255
    int Si; // 0 to 255
    int Li; // 0 to 255
    
    int A; // 0 to max independant of all other items
    
    // Call one of these whenever you change rgb or hsl
    void RGBtoHSL();
    void HSLtoRGB();
};

// RGBHSL selection widget

class WRGBSelect : public Widget {
private:
    enum {
        // Border
        WRGBSELECT_BORDER = 1,
        
        // Gutter between xy and z panels, in addition to borders
        WRGBSELECT_GUTTER = 8,
        
        // Width of z panel
        WRGBSELECT_ZWIDTH = 15,
        
        // XY panel cursor- thickness and length of each crosshair
        WRGBSELECT_CURSOR_THICK = 3,
        WRGBSELECT_CURSOR_LENGTH = 8,
    };
    
    int maxComponent;

    // x/y and z areas
    SDL_Surface* xyPanel;
    SDL_Surface* zPanel;
    
    // Partial dirties
    int dirtyXY;
    int dirtyZ;

    color currentColor;

    // What makes up each axis
    int aX;
    int aY;
    int aZ;
    
    // (axises flipped?)
    int fX;
    int fY;
    int fZ;
    
    // Does Z depend on XY? (if not, XY depends on Z)
    int dependXYtoZ;
    
    // (actual pixel location)
    int xSelected;
    int ySelected;
    int zSelected;
    
    int innerWidth;
    int innerHeight;
    int zPanelX;
    enum {
        WRGBSELECT_DRAG_NONE = 0,
        WRGBSELECT_DRAG_XY,
        WRGBSELECT_DRAG_Z,
    } dragMode;
    
    void locateColor(int fullredraw = 0);
    void redrawXYPanel();
    void redrawZPanel();
    static int* convertAxis(color* src, int axis);
    static int convertMax(const color* src, int axis);
    void displayCursor(SDL_Surface* destSurface, int xCenter, int yCenter);
    // (actual pixel location)
    void setXY(int newX, int newY);
    void setZ(int newZ);

public:
    // w/h of primary selection area; actual w/h may differ
    WRGBSelect(int wId, int w, int h, color* wSetting, int rgbBitDepth);
    ~WRGBSelect();
    
    // Set the X, Y, and Z (slider) axis
    // Must be either r/g/b in any order or h/s/l in any order
    enum {
        AXIS_R = 1,
        AXIS_G,
        AXIS_B,
        
        AXIS_H,
        AXIS_S,
        AXIS_L
    };
    void setMode(int axisX, int axisY, int axisZ, int flipX, int flipY, int flipZ, int setDependXYtoZ = 1);

    int event(int hasFocus, const SDL_Event* event);
    void load();
    void apply();
    void display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset);
    void resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp);
};


// Color display widget

class WShowColor : public Widget {
private:
    enum {
        // Border
        SHOWCOLOR_BORDER = 1,
    };

    color currentColor;

public:
    WShowColor(int wId, int w, int h, color* wSetting);
    ~WShowColor();

    int event(int hasFocus, const SDL_Event* event);
    int refuseAll() const;
    void load();
    void apply();
    void display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset);
};

// RGB selection dialog

class RGBSelect : public Dialog {
private:
    int initializedH; // The window height we initialized for

    color theColor;
    int rgbBitDepth;
    int alphaBitDepth;
    int method;

    enum {
        // Demo color
        ID_DEMOCOLOR,
    
        // Change RGB/HSL method; these IDs must all be in this order
        ID_METHOD,
        ID_METHOD_HS,
        ID_METHOD_LH,
        ID_METHOD_SL,
        ID_METHOD_RG,
        ID_METHOD_GB,
        ID_METHOD_BR,
    
        // RGB/HSL selector
        ID_RGBSELECT,
    
        // RGBRGB HSLHSL Must be here and in this numerical order
        ID_R_N,
        ID_G_N,
        ID_B_N,
        ID_R_S,
        ID_G_S,
        ID_B_S,
        ID_H_N,
        ID_S_N,
        ID_L_N,
        ID_H_S,
        ID_S_S,
        ID_L_S,
        
        ID_A_N,
        ID_A_S,
        
        // Labels
        ID_R_LABEL,
        ID_G_LABEL,
        ID_B_LABEL,
        ID_H_LABEL,
        ID_S_LABEL,
        ID_L_LABEL,
        ID_A_LABEL,
        
        ID_OK,
        ID_CANCEL,
    };
    
    void applyRGBMethod();
    
    SDL_Surface* iconSurface;
    static const std::string resourceError;

    static class RGBSelect* dialog;

public:
    RGBSelect();

    static class RGBSelect* create();
    static void destroy();

    void childModified(Window* modified);
    
    // Returns true if OK (false if cancelled)
    int run(unsigned char& r, unsigned char& g, unsigned char& b, unsigned char& a, int bitDepthAlpha = 8, int bitDepth = 8);
};

#endif

